﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Roslyn.Compilers.Common;
using Roslyn.Compilers.CSharp;
using FernUniHagen.CompilerExtension.Functions;

namespace FernUniHagen.CompilerExtension.Converter
{
    /// <summary>
    /// @Text: http://msdn.microsoft.com/de-de/library/vstudio/dd145423.aspx begründung für implementierung von IEnumerable
    /// TODO: Prüfen ob auch IENumerator implementiert werden muss
    /// TODO: Was sol mit externen Klassen gemacht werden? Erben und erweitern? Neben Sealed besteht hier auch ein problem mit Privaten Methoden 
    /// wenn die Klasse sich selbst referenziert
    /// TODO: Evtl. könnte auch ein ansatz verfolgt werden in dem die betreffende Variable einfach in eine Liste vor dem LINQ ausdruck gespeichert wird
    /// TODO: Was passiert wenn Klasse bereits interface erbt? Ebenfalls Erben und erweitern?
    /// 
    /// Class applies the folowing Conversion Rule:
    /// Conversion Rule applies the following:
    /// In Case of Option in query (e.g. from t in b) where b has multiplicity option
    /// 1.find the defining class of b
    /// 2.Let the class inherit from IEnumerable(classtype)
    /// 3.Implement the ienumerable interface in class
    ///     3.a content of this method will be a list which is initialised with (this element)
    ///     3.b the enumerator of this list will be returned
    /// </summary>

    /// <returns></returns>
    public class ClassDeclarationConverter : NodeConverterFactory
    {
        /// <summary>
        /// Implements converting rules to support Linq statements on variables which are declared with optional multiplicity
        /// </summary>
        /// <returns>A classdeclaration which inherits the ienumerable interface</returns>
        public override CommonSyntaxNode Convert()
        {
            //Create values we need like the Name of the class and the generic IEnumerable Type
            var classDeclaration = Utilities.CastNode<ClassDeclarationSyntax>(Node);
            var className = classDeclaration.Identifier.ToString();
            TypeSyntax inheritedType = Syntax.ParseTypeName(string.Format("System.Collections.Generic.IEnumerable<{0}>", className));
            TypeSyntax returnType = Syntax.ParseTypeName(string.Format("System.Collections.Generic.IEnumerator<{0}>", className));
            BaseListSyntax inheritedClasses = classDeclaration.BaseList;
           


            //in case we have already added a IEnumerable we can return
            //TODO: String compare used; consider a better design
            if (inheritedClasses.Types.Any(n => n.ToString().Equals(inheritedType.ToString()))) return classDeclaration;

            //Add the type to the inherited list
            inheritedClasses = inheritedClasses.AddTypes(inheritedType);
            classDeclaration = classDeclaration.WithBaseList(inheritedClasses);

   
         

            //initial idea from: http://blog.diniscruz.com/2012/06/using-roslyn-refactoring.html
            //Now we need to implement the methods provided by the interface
            //First we start with the public IEnumerator<T> GetEnumerator Method
            var returnSpecificEnumeratorStmt =Syntax.ParseStatement(string.Format("return new System.Collections.Generic.List<A> {{ this }}.GetEnumerator();" + Environment.NewLine));

            var getEnumeratorBlock = Syntax.Block(returnSpecificEnumeratorStmt);
            var getEnumeratorMethod = Syntax.MethodDeclaration(Syntax.ParseTypeName(returnType.ToString()), "GetEnumerator");
            getEnumeratorMethod = getEnumeratorMethod.WithBody(getEnumeratorBlock);
            getEnumeratorMethod = getEnumeratorMethod.WithModifiers(Syntax.ParseToken("public "));

            //Now we need the System.Collections.IEnumerator IEnumerable.GetEnumerator Method
            var returnCommonEnumeratorStmt = Syntax.ParseStatement(string.Format("return (System.Collections.IEnumerator)GetEnumerator();" + Environment.NewLine));
            var ienumerableEnumeratorBlock = Syntax.Block(returnCommonEnumeratorStmt);
            var getIenumMethod = Syntax.MethodDeclaration(Syntax.ParseTypeName("System.Collections.IEnumerator"), "System.Collections.IEnumerable.GetEnumerator");
            getIenumMethod = getIenumMethod.WithBody(ienumerableEnumeratorBlock);

            classDeclaration = classDeclaration.AddMembers(getEnumeratorMethod, getIenumMethod); 

            return classDeclaration;
        }
    }
}
